#include <cstdlib>
#include <getopt.h>
#include <iostream>
#include <string>

#include "properties_reader.h"

#include "routing/routing_io.h"
#include "wave/wave_io.h"
#ifdef _STEGIT_ALSA_SUPPORT
#include "sound/alsa_io.h"
#endif
#ifdef _STEGIT_PULSE_SUPPORT
#include "sound/pulse_audio_io.h"
#endif
#include "socket_io.h"
#include "combination_io.h"
#include "deinterleave_io.h"
#include "original_modified_io.h"
#include "engine_factory.h"
#include "types.h"
#include "logger.h"
#include "stegit_framework.h"
#include "samples_preprocessor.h"
#include "ui_socket.h"
#include "crypto/crypto_engines.h"

#define OPTION_STRING "hdc:r:w:R:W:1:2:3:4:e:p:is:E:b:D:o:"

#undef VERBOSE_DEBUG

using namespace std;

enum EIOType {
	IO_NONE = 1,
	IO_WAVE_MONO,
	IO_WAVE_STEREO_L,
	IO_WAVE_STEREO_R,
	IO_ALSA,
	IO_PULSE,
	IO_ROUTING,
	IO_SOCKET,
};

struct SIOConfig
{
	string mIniFilename;
	bool mDisableEmbedExtract;
	bool mReplayWave;
	double mDiscardTime;
	unsigned int mExtractBufferSize;
	EIOType mReadEmbed;
	EIOType mWriteEmbed;
	EIOType mReadExtract;
	EIOType mWriteExtract;
	string mOptionsREm;
	string mOptionsWEm;
	string mOptionsREx;
	string mOptionsWEx;

	SIOConfig()
			:mIniFilename("/etc/stegit-2/stegit-2.conf"),
			mDisableEmbedExtract(false),
			mReplayWave(false),
			mDiscardTime(0.0),
			mExtractBufferSize(1024*1024*1.5),
			mReadEmbed(IO_NONE),
			mWriteEmbed(IO_NONE),
			mReadExtract(IO_NONE),
			mWriteExtract(IO_NONE),
			mOptionsREm(""),
			mOptionsWEm(""),
			mOptionsREx(""),
			mOptionsWEx("")
	{}
};

bool getOptions(int argc, char* argv[], SIOConfig &pIOConf);
bool editConfig(int argc, char* argv[], CPropertiesReader &pConfig);

EIOType getIoType(char *ioType);
ISampleIO* getNewIOInstance(SIOConfig &pIOConf, CPropertiesReader* pReader);

static void sigInterruptHandler(int pParam);

static bool sRunning = true;

int main(int argc, char* argv[])
{
#if 0
	init_log("no log file used", LT_STDERR | L_DATE | L_TIME | LL_DEBUG);
#else
	init_log(CLogger::LOG_SOCKET);
#endif

	printl(0, __FILE__, "StegIT-2 started\n");

	struct sigaction lActions;
	memset(&lActions, 0, sizeof(lActions));
	sigemptyset(&lActions.sa_mask);
	lActions.sa_flags = 0;
	lActions.sa_handler = sigInterruptHandler;

	if(sigaction(SIGINT, &lActions, NULL) == -1) {
		printl(LL_ERR, __FILE__, "Changing the signal handler failed.\n");
		cleanup_log();
		return EXIT_FAILURE;
	}
		
	SIOConfig lIOConf;
	getOptions(argc, argv, lIOConf);

	CPropertiesReader* lReader = new CPropertiesReader(lIOConf.mIniFilename, OPEN_NOAUTOLOAD);
	if(!lReader->loadIniFile()) {
		printl(LL_ERR, __FILE__, "INI file '%s' not found or has an error\n",
				lIOConf.mIniFilename.c_str());
		delete lReader;
		cleanup_log();
		return EXIT_FAILURE;
	}

	if(!editConfig(argc, argv, *lReader)) {
		printl(LL_ERR, __FILE__, "Changing config failed.\n");
		delete lReader;
		cleanup_log();
		return EXIT_FAILURE;
	}

	getOptions(argc, argv, lIOConf);

	CUISocket* lUI = CUISocket::getInstance();
	lUI->setRunning(&sRunning);
	lUI->setPropertiesReader(lReader);
	if(!lUI->startUIServer())
	{
		printl(LL_ERR, __FILE__, "Could not start the User Interface properly\n");
		CUISocket::destroy();
		delete lReader;
		cleanup_log();
		return EXIT_FAILURE;
	}

	printl(LL_ERR, __FILE__, "Wait for User Interface communication and start signal ...\n");
	if((!lUI->waitForUIClientAndStartSignal()) || !sRunning) {
		printl(LL_ERR, __FILE__, "Wait finished but no start signal\n");
		CUISocket::destroy();
		delete lReader;
		cleanup_log();
		return EXIT_FAILURE;
	}
	printl(0, __FILE__, "Wait finished\n");
	ISampleIO* lIO = getNewIOInstance(lIOConf, lReader);
	if(!lIO) {
		CUISocket::destroy();
		delete lReader;
		cleanup_log();
		return EXIT_FAILURE;
	}

	if(lIO->init(lReader) != 0) {
		printl(LL_ERR, __FILE__, "IO init failed\n");
		lIO->destroy();
		delete lIO;
		CUISocket::destroy();
		delete lReader;
		cleanup_log();
		return EXIT_FAILURE;
	}

	CStegCallHandle* lCallHandle = NULL;

	lCallHandle = CEngineFactory::createCallHandle("testhandle",
			lReader,
			lIO->getStreamInfo());

	/* rpoisel: the stream-type is not known at the beginning of a VoIP transmission */
	/* TODO determine stream-type from configuration */
	CSamplesPreprocessor* lSamplesPreprocessor(NULL);
	try
	{
		lSamplesPreprocessor = new CSamplesPreprocessor(
				lIO->getStreamInfo(),
				lCallHandle,
				lIOConf.mExtractBufferSize,
				lReader);
	}
	catch(std::bad_alloc& pException)
	{
		printl(LOG_ERR, __FILE__, "SamplesPreprocessor seems to request too much memory. \n");
		lIO->destroy();
		CEngineFactory::destroyCallHandle(&lCallHandle);
		delete lReader;
		cleanup_log();
		return EXIT_FAILURE;
	}

	int lCount = 0;
	void *lData(NULL);
	ISampleIO::EAction lAction(ISampleIO::UNKNOWN_ACTION);

	bool lNoEOF(true);
	while (sRunning && lNoEOF)
	{
		lCount = lIO->read(&lData, lAction);
	
		if (lCount == 0 && lAction == ISampleIO::NO_ACTION) {
			continue; /* nothing read until timeout reached */
		}

		if (lCount <= 0) {
			break;
		}

		int rv(-1);
		if(lIOConf.mDisableEmbedExtract) {
			//printl(LL_WARN, __FILE__, "Embedding and extracting are disabled.\n");
			rv = 1;
		}
		else {
			switch (lAction) {
				case ISampleIO::EMBED:
#ifdef VERBOSE_DEBUG
					printl(LL_DEBUG, __FILE__, "Embedding into %d bytes of data. \n", lCount);
#endif
					rv = lSamplesPreprocessor->createStegoObject(lData, lCount, 0);
					if(rv < 0)
					{
#ifdef VERBOSE_DEBUG
						printl(LL_ERR, __FILE__, "Embed failed. \n");
#endif
					}
					break;
				case ISampleIO::EXTRACT:
#ifdef VERBOSE_DEBUG
					printl(LL_DEBUG, __FILE__, "Extracting out of %d bytes of data. \n", lCount);
#endif
					rv = lSamplesPreprocessor->extractFromStegoObject(lData, lCount, 0);
					if(rv < 0)
					{
#ifdef VERBOSE_DEBUG
						printl(LL_ERR, __FILE__, "Extract failed. \n");
#endif
					}
					break;
				default:
					printl(LL_DEBUG, __FILE__, "Nothing to do. Action: %d\n",
							lAction);
					break;
			}
		}

		int lWriteCount;
		if((lWriteCount = lIO->write(lData, lAction, lCount)) != lCount) {
			printl(LL_WARN, __FILE__, "Unbalanced write: # of bytes written(%d), "
				"# of bytes supposed to write(%d). \n", lWriteCount, lCount);
			if(lWriteCount <= 0) {
				break;
			}
		}
		//printl(0, __FILE__, "Write %d bytes, action: %d\n", lWriteCount, lAction);

		if(rv == 0) {
			switch(lAction)
			{
				case ISampleIO::EMBED:
					printl(0, __FILE__, "createStegoObject() returned EOF\n");
					lNoEOF = false;
					break;
				case ISampleIO::EXTRACT:
					//printl(LL_WARN, __FILE__, "extractFromStegoObject() returned 0\n");
					break;
				default:
					/* other actions go here */
					break;
			}
		}
	}

	if(!sRunning) {
		lSamplesPreprocessor->setExitForced();

		printl(0, __FILE__, "Caught signal.\n");
	}

	delete lSamplesPreprocessor;
	lIO->destroy();
	CEngineFactory::destroyCallHandle(&lCallHandle);

	delete lIO;
	CUISocket::destroy();
	delete lReader;

	printl(0, __FILE__, "StegIT-2 finished\n");
	cleanup_log();
	return EXIT_SUCCESS;
}

bool getOptions(int argc, char* argv[], SIOConfig &pIOConf)
{
	optind = 1; // reset getopt
	int lOption(0);
	while((lOption = getopt(argc, argv, OPTION_STRING)) != -1)
	{
		switch(lOption) {
			case '?': {
				printl(0, __FILE__, "Use -h for help\n");
				return false;
			}
			case 'd': {
				printl(0, __FILE__, "Disable embedding and extracting.\n");
				pIOConf.mDisableEmbedExtract = true;
				break;
			}
			case 'c': {
				pIOConf.mIniFilename = optarg;
				break;
			}
			case 'r': {
				pIOConf.mReadEmbed = getIoType(optarg);
				break;
			}
			case 'w': {
				pIOConf.mWriteEmbed = getIoType(optarg);
				break;
			}
			case 'R': {
				pIOConf.mReadExtract = getIoType(optarg);
				break;
			}
			case 'W': {
				pIOConf.mWriteExtract = getIoType(optarg);
				break;
			}
			case '1': {
				pIOConf.mOptionsREm = optarg;
				break;
			}
			case '2': {
				pIOConf.mOptionsWEm = optarg;
				break;
			}
			case '3': {
				pIOConf.mOptionsREx = optarg;
				break;
			}
			case '4': {
				pIOConf.mOptionsWEx = optarg;
				break;
			}
			case 'i': {
				pIOConf.mReplayWave = true;
				break;
			}
			case 'D': {
				pIOConf.mDiscardTime = atof(optarg) / 1000.0;
				break;
			}
			case 's': {
				pIOConf.mExtractBufferSize = atoi(optarg) * 1024 * 1024;
				break;
			}
			default:
				// e, p will be observed by editConfig
				break;
		}
	}
	return true;
}

bool editConfig(int argc, char* argv[], CPropertiesReader &pConfig)
{
	optind = 1; // reset getopt
	int lOption(0);
	while((lOption = getopt(argc, argv, OPTION_STRING)) != -1)
	{
		switch(lOption) {
			case '?': {
				printl(0, __FILE__, "Use -h for help\n");
				break;
			}
			case 'e': {
				string lOptarg(optarg);
				size_t lFindIndex;
				lFindIndex = lOptarg.find("::");
				if(lFindIndex == string::npos) {
					printl(0, __FILE__, "Wrong syntax: %s\n", lOptarg.c_str());
					break;
				}
				string lSection = lOptarg.substr(0, lFindIndex);
				lOptarg = lOptarg.substr(lFindIndex + 2);
				lFindIndex = lOptarg.find("=");
				if(lFindIndex == string::npos) {
					printl(0, __FILE__, "Wrong syntax: %s::%s\n",
							lSection.c_str(),
							lOptarg.c_str());
					break;
				}
				string lKey = lOptarg.substr(0, lFindIndex);
				lOptarg = lOptarg.substr(lFindIndex + 1);


				if(!pConfig.containsSection(lSection)){
					pConfig.createSection(lSection);
				}

				if(!pConfig.containsKey(lSection,lKey)){
					pConfig.createKey(lSection,lKey,lOptarg);
				}else {
					if(!pConfig.setValue(lSection, lKey, lOptarg)) {
						printl(LL_WARN, __FILE__, "Can't change key %s at section [%s]\n",
								lKey.c_str(),
								lSection.c_str());
						break;
					}
				}


				printl(0, __FILE__, "Change key %s (section [%s]) to %s\n",
						lKey.c_str(),
						lSection.c_str(),
						lOptarg.c_str());
				break;
			}
			case 'p': {
				if (!optarg || optarg[0] == '\0') {
					break;
				}
				if (optarg[1] != '\0') {
					printl(LL_WARN, __FILE__, "Too long option argument for -p.\n");
					break;
				}
				string lValue("DEFAULT");
				switch(optarg[0]) {
					case 'f':
						lValue = "FILE";
						break;
					case 's':
						lValue = "SIMPLEFILE";
						break;
					case 'g':
						lValue = "GUI";
						break;
					default:
						break;
				}
				if(!pConfig.setValue(CEngineFactory::sIniSectionName, "PROTOCOL_ENGINE", lValue)) {
					printl(LL_WARN, __FILE__, "Can't change key PROTOCOL_ENGINE at section [%s]\n", 
							CEngineFactory::sIniSectionName.c_str());
				}
				break;
			}
			case 'E': {
				if (!optarg || optarg[0] == '\0') {
					break;
				}
				if (optarg[1] != '\0') {
					printl(LL_WARN, __FILE__, "Too long option argument for -E.\n");
					break;
				}
				string lValue("DEFAULT");
				switch(optarg[0]) {
					case 'l':
						lValue = "LSB";
						break;
					case 'r':
						lValue = "LSB_RAND";
						break;
					case 'c':
						lValue = CE_NULL_CYPHER;
						break;
					case 'a':
						lValue = CE_AMPLITUDE_SCRAMBLING;
						break;
					case 'A':
						lValue = CE_ADV_AMPLITUDE_SCRAMBLING;
						break;
					case 'f':
						lValue = CE_FREQUENCY_PERMUTATION;
						break;
					default:
						printl(LL_ERR, __FILE__, "unsupported value for option -E.\n");
						return false;
				}
				if(!pConfig.setValue(CEngineFactory::sIniSectionName, "EMBEDDING_ENGINE", lValue)) {
					printl(LL_WARN, __FILE__, "Can't change key EMBEDDING_ENGINE at section [%s]\n",
							CEngineFactory::sIniSectionName.c_str());
				}
				break;
			}
			case 'b': {
				if (!optarg || optarg[0] == '\0') {
					break;
				}
				if (optarg[1] != '\0') {
					printl(LL_WARN, __FILE__, "Too long option argument for -b.\n");
					break;
				}
				string lValue("DEFAULT");
				switch(optarg[0]) {
					case 'b':
						lValue = "BYTE_BUILDER";
						break;
					case 'f':
						lValue = "FRAME_BUILDER";
						break;
					case 's':
						lValue = "STREAM_BUILDER";
						break;
					default:
						break;
				}
				if(!pConfig.setValue(CEngineFactory::sIniSectionName, "BIT_SOURCE", lValue)) {
					printl(LL_WARN, __FILE__, "Can't change key BIT_SOURCE at section [%s]\n",
							CEngineFactory::sIniSectionName.c_str());
				}
				break;
			}
			case 'o':
				std::string lSection;
				string lOptarg(optarg);
				size_t foundDot=lOptarg.find("."),foundEqual=lOptarg.find("=");

				if(foundDot==string::npos||foundEqual==string::npos||foundDot>foundEqual){
					printl(LL_WARN, __FILE__, "could not parse option %s",lOptarg.data());
				}else {
					std::string section=lOptarg.substr(0,foundDot);
					std::string param=lOptarg.substr(foundDot+1,foundEqual-foundDot-1);
					std::string value=lOptarg.substr(foundEqual+1);

					if(!pConfig.containsSection(section)){
						pConfig.createSection(section);
					}

					if(!pConfig.containsKey(section,param)){
						pConfig.createKey(section,param,"");
					}

					if(!pConfig.setValue(section,param, value)) {
						printl(LL_WARN, __FILE__, "Can't change key %s at section [%s] to %s\n",param.c_str(),section.c_str(),value.c_str());
					}else {
						printl(LL_DEBUG, __FILE__, "set %s.%s to %s\n",section.c_str(),param.c_str(),value.c_str());
					}
				}



				break;
		}
	}
	return true;
}

EIOType getIoType(char *ioType)
{
	if(!ioType) {
		return IO_NONE;
	}
	string s(ioType);
	if(s == "n") {
		return IO_NONE;
	}
	if(s == "wm") {
		return IO_WAVE_MONO;
	}
	if(s == "wsl") {
		return IO_WAVE_STEREO_L;
	}
	if(s == "wsr") {
		return IO_WAVE_STEREO_R;
	}
	if(s == "a") {
		return IO_ALSA;
	}
	if(s == "p") {
		return IO_PULSE;
	}
	if(s == "r") {
		return IO_ROUTING;
	}
	if(s == "s") {
		return IO_SOCKET;
	}
	printl(0, __FILE__, "Unsupported IO type\n");
	return IO_NONE;
}

ISampleIO* getNewIOInstance(SIOConfig &pIOConf, CPropertiesReader* pReader)
{
	if(pIOConf.mReadEmbed == IO_ROUTING
	&& pIOConf.mWriteEmbed == IO_ROUTING
	&& pIOConf.mReadExtract == IO_ROUTING
	&& pIOConf.mWriteExtract == IO_ROUTING) {
		printl(0, __FILE__, "Create CRoutingIO\n");
		return new CRoutingIO();
	}
	/*
	if(pIOConf.mReadEmbed == IO_WAVE_STEREO_L
	&& pIOConf.mWriteEmbed == IO_WAVE_STEREO_L
	&& pIOConf.mReadExtract == IO_WAVE_STEREO_R
	&& pIOConf.mWriteExtract == IO_WAVE_STEREO_R) {
		printl(0, __FILE__, "Create CWaveIO\n");
		return new CWaveIO();
	}*/
#ifdef _STEGIT_PULSE_SUPPORT
	if(pIOConf.mReadEmbed == IO_WAVE_MONO
	&& pIOConf.mWriteEmbed == IO_SOCKET
	&& pIOConf.mReadExtract == IO_SOCKET
	&& pIOConf.mWriteExtract == IO_PULSE) {
		printl(0, __FILE__, "Create CSocketIO\n");
		CWaveIO* lWaveIO = new CWaveIO(ISampleIO::EMBED, pIOConf.mOptionsREm, pIOConf.mReplayWave);
		CPulseAudioIO* lPulseIO = new CPulseAudioIO(ISampleIO::EXTRACT, 1, lWaveIO->getStreamInfo());
		CCombinationIO* lCombiIO = new CCombinationIO(lWaveIO, lPulseIO);
		CSocketIO* lSocketIO = new CSocketIO(lWaveIO->getStreamInfo());
		return new COriginalModifiedIO(lCombiIO, lSocketIO);
	}
#endif
#ifdef _STEGIT_ALSA_SUPPORT
	if(pIOConf.mReadEmbed == IO_WAVE_MONO
	&& pIOConf.mWriteEmbed == IO_SOCKET
	&& pIOConf.mReadExtract == IO_SOCKET
	&& pIOConf.mWriteExtract == IO_ALSA) {
		printl(0, __FILE__, "Create CSocketIO\n");
		CWaveIO* lWaveIO = new CWaveIO(ISampleIO::EMBED, pIOConf.mOptionsREm, pIOConf.mReplayWave);
		CAlsaIO* lAlsaIO = new CAlsaIO(ISampleIO::EXTRACT, 1, lWaveIO->getStreamInfo());
		CCombinationIO* lCombiIO = new CCombinationIO(lWaveIO, lAlsaIO);
		CSocketIO* lSocketIO = new CSocketIO(lWaveIO->getStreamInfo());
		return new COriginalModifiedIO(lCombiIO, lSocketIO);
	}
#endif
	if(pIOConf.mReadEmbed == IO_WAVE_MONO
	&& pIOConf.mWriteEmbed == IO_WAVE_MONO
	&& pIOConf.mReadExtract == IO_NONE
	&& pIOConf.mWriteExtract == IO_NONE) {
		printl(0, __FILE__, "Create CWaveIO, EMBED mono\n");
		return new CWaveIO(ISampleIO::EMBED, pIOConf.mOptionsREm, pIOConf.mOptionsWEm, pIOConf.mDiscardTime);
	}
	if(pIOConf.mReadEmbed == IO_NONE
	&& pIOConf.mWriteEmbed == IO_NONE
	&& pIOConf.mReadExtract == IO_WAVE_MONO
	&& pIOConf.mWriteExtract == IO_WAVE_MONO) {
		printl(0, __FILE__, "Create CWaveIO, EXTRACT mono\n");
		return new CWaveIO(ISampleIO::EXTRACT, pIOConf.mOptionsREx, pIOConf.mOptionsWEx, pIOConf.mDiscardTime);
	}
#ifdef _STEGIT_ALSA_SUPPORT
	if(pIOConf.mReadEmbed == IO_WAVE_MONO
	&& pIOConf.mWriteEmbed == IO_ALSA
	&& pIOConf.mReadExtract == IO_NONE
	&& pIOConf.mWriteExtract == IO_NONE) {
		printl(0, __FILE__, "Create CCombinationIO, EMBED mono wav, playback\n");
		CWaveIO* lWaveIO = new CWaveIO(ISampleIO::EMBED, pIOConf.mOptionsREm, pIOConf.mReplayWave);
		CAlsaIO* lAlsaIO = new CAlsaIO(ISampleIO::EMBED, 1, lWaveIO->getStreamInfo());
		return new CCombinationIO(lWaveIO, lAlsaIO);
	}
	if(pIOConf.mReadEmbed == IO_ALSA
	&& pIOConf.mWriteEmbed == IO_WAVE_MONO
	&& pIOConf.mReadExtract == IO_NONE
	&& pIOConf.mWriteExtract == IO_NONE) {
		printl(0, __FILE__, "Create CCombinationIO, capture, EMBED mono wav\n");
		CAlsaIO* lAlsaIO = new CAlsaIO(ISampleIO::EMBED, 1);
		CWaveIO* lWaveIO = new CWaveIO(ISampleIO::EMBED,
				pIOConf.mOptionsWEm,
				lAlsaIO->getStreamInfo());
		return new CCombinationIO(lAlsaIO, lWaveIO);
	}
	if(pIOConf.mReadEmbed == IO_NONE
	&& pIOConf.mWriteEmbed == IO_NONE
	&& pIOConf.mReadExtract == IO_WAVE_MONO
	&& pIOConf.mWriteExtract == IO_ALSA) {
		printl(0, __FILE__, "Create CCombinationIO, EXTRACT mono wav, playback\n");
		CWaveIO* lWaveIO = new CWaveIO(ISampleIO::EXTRACT, pIOConf.mOptionsREx, pIOConf.mReplayWave);
		CAlsaIO* lAlsaIO = new CAlsaIO(ISampleIO::EXTRACT, 1, lWaveIO->getStreamInfo());
		return new CCombinationIO(lWaveIO, lAlsaIO);
	}
	if(pIOConf.mReadEmbed == IO_ALSA
	&& pIOConf.mWriteEmbed == IO_ALSA
	&& pIOConf.mReadExtract == IO_NONE
	&& pIOConf.mWriteExtract == IO_NONE) {
		printl(0, __FILE__, "Create CAlsaIO, EMBED, playback\n");
		return new CAlsaIO(ISampleIO::EMBED);
	}
	if(pIOConf.mReadEmbed == IO_NONE
	&& pIOConf.mWriteEmbed == IO_NONE
	&& pIOConf.mReadExtract == IO_ALSA
	&& pIOConf.mWriteExtract == IO_ALSA) {
		printl(0, __FILE__, "Create CAlsaIO, EXTRACT, playback\n");
		return new CAlsaIO(ISampleIO::EXTRACT);
	}
	if(pIOConf.mReadEmbed == IO_ALSA
	&& pIOConf.mWriteEmbed == IO_ALSA
	&& pIOConf.mReadExtract == IO_ALSA
	&& pIOConf.mWriteExtract == IO_ALSA) {
		printl(0, __FILE__, "Create CAlsaIO, EMBED + EXTRACT, playback\n");
		return new CDeinterleaveIO(new CAlsaIO(ISampleIO::INTERLEAVED_EMBED_EXTRACT));
	}
#endif
#ifdef _STEGIT_PULSE_SUPPORT
	if(pIOConf.mReadEmbed == IO_WAVE_MONO
	&& pIOConf.mWriteEmbed == IO_PULSE
	&& pIOConf.mReadExtract == IO_NONE
	&& pIOConf.mWriteExtract == IO_NONE) {
		printl(0, __FILE__, "Create CCombinationIO, EMBED mono wav, playback\n");
		CWaveIO* lWaveIO = new CWaveIO(ISampleIO::EMBED, pIOConf.mOptionsREm, pIOConf.mReplayWave);
		CPulseAudioIO* lPulseIO = new CPulseAudioIO(ISampleIO::EMBED, 1, lWaveIO->getStreamInfo());
		/*CPulseAudioIO* lPulseIO = new CPulseAudioIO(ISampleIO::EMBED,
				1,
				NAudioFormats::SStreamInfo(NAudioFormats::U8,
						lWaveIO->getStreamInfo().mSampleRate,
						NAudioFormats::ALAW));*/
		return new CCombinationIO(lWaveIO, lPulseIO);
	}
	if(pIOConf.mReadEmbed == IO_PULSE
	&& pIOConf.mWriteEmbed == IO_WAVE_MONO
	&& pIOConf.mReadExtract == IO_NONE
	&& pIOConf.mWriteExtract == IO_NONE) {
		printl(0, __FILE__, "Create CCombinationIO, capture, EMBED mono wav\n");
		CPulseAudioIO* lPulseIO = new CPulseAudioIO(ISampleIO::EMBED, 1);
		CWaveIO* lWaveIO = new CWaveIO(ISampleIO::EMBED,
				pIOConf.mOptionsWEm,
				lPulseIO->getStreamInfo());
		return new CCombinationIO(lPulseIO, lWaveIO);
	}
	if(pIOConf.mReadEmbed == IO_NONE
	&& pIOConf.mWriteEmbed == IO_NONE
	&& pIOConf.mReadExtract == IO_WAVE_MONO
	&& pIOConf.mWriteExtract == IO_PULSE) {
		printl(0, __FILE__, "Create CCombinationIO, EXTRACT mono wav, playback\n");
		CWaveIO* lWaveIO = new CWaveIO(ISampleIO::EXTRACT, pIOConf.mOptionsREx, pIOConf.mReplayWave);
		CPulseAudioIO* lPulseIO = new CPulseAudioIO(ISampleIO::EXTRACT, 1, lWaveIO->getStreamInfo());
		/*CPulseAudioIO* lPulseIO = new CPulseAudioIO(ISampleIO::EXTRACT,
				1,
				NAudioFormats::SStreamInfo(NAudioFormats::S16HO,
						lWaveIO->getStreamInfo().mSampleRate,
						NAudioFormats::PCM));*/
		return new CCombinationIO(lWaveIO, lPulseIO);
	}
	if(pIOConf.mReadEmbed == IO_PULSE
	&& pIOConf.mWriteEmbed == IO_PULSE
	&& pIOConf.mReadExtract == IO_NONE
	&& pIOConf.mWriteExtract == IO_NONE) {
		printl(0, __FILE__, "Create CPulseAudioIO, EMBED, playback\n");
		return new CPulseAudioIO(ISampleIO::EMBED);
	}
	if(pIOConf.mReadEmbed == IO_NONE
	&& pIOConf.mWriteEmbed == IO_NONE
	&& pIOConf.mReadExtract == IO_PULSE
	&& pIOConf.mWriteExtract == IO_PULSE) {
		printl(0, __FILE__, "Create CPulseAudioIO, EXTRACT, playback\n");
		return new CPulseAudioIO(ISampleIO::EXTRACT);
	}
	if(pIOConf.mReadEmbed == IO_PULSE
	&& pIOConf.mWriteEmbed == IO_PULSE
	&& pIOConf.mReadExtract == IO_PULSE
	&& pIOConf.mWriteExtract == IO_PULSE) {
		printl(0, __FILE__, "Create CPulseAudioIO, EMBED + EXTRACT, playback\n");
		return new CDeinterleaveIO(new CPulseAudioIO(ISampleIO::INTERLEAVED_EMBED_EXTRACT));
	}
#endif
	if(pIOConf.mReadEmbed == IO_WAVE_STEREO_L
	&& pIOConf.mWriteEmbed == IO_WAVE_STEREO_L
	&& pIOConf.mReadExtract == IO_WAVE_STEREO_R
	&& pIOConf.mWriteExtract == IO_WAVE_STEREO_R) {
		printl(0, __FILE__, "Create CDeinterleaveIO, stereo\n");
		return new CDeinterleaveIO(new CWaveIO(ISampleIO::INTERLEAVED_EMBED_EXTRACT, pIOConf.mOptionsREm, pIOConf.mOptionsWEm, pIOConf.mDiscardTime));
	}
	for(int i = 0; i < 4; i++) {
		EIOType lIOType(IO_NONE);
		switch(i) {
			case 0:
				lIOType = pIOConf.mReadEmbed;
				break;
			case 1:
				lIOType = pIOConf.mWriteEmbed;
				break;
			case 2:
				lIOType = pIOConf.mReadExtract;
				break;
			case 3:
				lIOType = pIOConf.mWriteExtract;
				break;
		}
		switch(lIOType) {
			case IO_NONE:
				break;
			case IO_WAVE_MONO:
				break;
			case IO_WAVE_STEREO_L:
				break;
			case IO_WAVE_STEREO_R:
				break;
			case IO_ALSA:
#				ifndef _STEGIT_ALSA_SUPPORT
				printl(LL_ERR, __FILE__, "This stegit version has no ALSA support.\n");
#				endif
				break;
			case IO_PULSE:
#				ifndef _STEGIT_PULSE_SUPPORT
				printl(LL_ERR, __FILE__, "This stegit version has no pulse audio support.\n");
#				endif
				break;
			case IO_ROUTING:
				break;
			case IO_SOCKET:
				break;
		}
	}
	printl(0, __FILE__, "No IO for this combination possible\n");
	return NULL;
}

void sigInterruptHandler(int pParam)
{
	sRunning = false;
}
